package com.dly.blog.service.impl;

import cn.hutool.core.collection.ListUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dly.blog.domain.*;
import com.dly.blog.domain.ro.PageRO;
import com.dly.blog.domain.ro.UserCreateRO;
import com.dly.blog.domain.ro.user.UserAddRO;
import com.dly.blog.domain.ro.user.UserEditRO;
import com.dly.blog.domain.ro.user.UserPageRO;
import com.dly.blog.domain.vo.*;
import com.dly.blog.dto.email.FileDTO;
import com.dly.blog.exception.AuthExceptionEnum;
import com.dly.blog.exception.UserExceptionEnum;
import com.dly.blog.mapper.*;
import com.dly.blog.provider.file.FileServiceProvider;
import com.dly.blog.provider.user.UserProvider;
import com.dly.blog.service.UserService;
import com.dly.blog.utile.SecurityUtils;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
* @author qq215
* @description 针对表【blog_user】的数据库操作Service实现
* @createDate 2023-07-10 20:39:20
*/
@Service
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    private final RoleMapper roleMapper;

    private final PermissionMapper permissionMapper;

    private final FileServiceProvider fileServiceProvider;

    private final UserRoleMapper userRoleMapper;

    private final PasswordEncoder passwordEncoder;

    private final UserProvider userProvider;

    private final MenuMapper menuMapper;

    @Override
    public UserVO getUserInfo() {
        String userId = SecurityUtils.getUserId();
        User user = baseMapper.selectById(userId);
        return Optional.ofNullable(user).map(userInfo->{
            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(userInfo, userVO);
            userVO.setId(userInfo.getId());
            if(Objects.nonNull(userInfo.getAvatar())) {
                List<FileDTO> file = fileServiceProvider.getFile(List.of(userInfo.getAvatar()));
                if (Objects.nonNull(file) && !file.isEmpty()) {
                    userVO.setAvatar(file.get(0).getUrl());
                }
            }
            List<Role> roles = roleMapper.searchAllByUserId(userId);
            List<String> roleIds = roles.stream().map(Role::getId).toList();
            List<Permission> permissions = permissionMapper.searchAllByRoleIds(roleIds);
            List<RoleVO> roleVOS = roles.stream().map(role -> {
                RoleVO roleVO = new RoleVO();
                BeanUtils.copyProperties(role, roleVO);
                return roleVO;
            }).toList();

            List<PermissionVO> permissionVOS = permissions.stream().map(permission -> {
                PermissionVO permissionVO = new PermissionVO();
                BeanUtils.copyProperties(permission, permissionVO);
                return permissionVO;
            }).toList();

            userVO.setRoleVOList(roleVOS);
            userVO.setPermissionVOList(permissionVOS);
            return userVO;
        }).orElseThrow(AuthExceptionEnum.USER_NO::getAuthException);
    }

    @Override
    public PageVO<UserVO> getUserPage(UserPageRO pageRO) {
        Page<User> userPage = baseMapper.selectPage(pageRO.buildIPage(), new LambdaQueryWrapper<User>().orderByDesc(User::getCreateTime));
        return PageVO.buildMyBatis(userPage, user -> {
            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(user, userVO);
            userVO.setId(user.getId());
            userVO.setUserRoles(roleMapper.searchAllRoleCodeByUserId(user.getId()));
            return userVO;
        });
    }

    @Override
    public AdminUserVO getAdminUserInfo() {
        String userId = SecurityUtils.getUserId();
        User user = baseMapper.selectById(userId);
        return Optional.ofNullable(user).map(userInfo->{
            AdminUserVO adminUserVO = new AdminUserVO();
            BeanUtils.copyProperties(userInfo, adminUserVO);
            adminUserVO.setId(userInfo.getId());
            if(Objects.nonNull(userInfo.getAvatar())) {
                List<FileDTO> file = fileServiceProvider.getFile(List.of(userInfo.getAvatar()));
                if (Objects.nonNull(file) && !file.isEmpty()) {
                    adminUserVO.setAvatar(file.get(0).getUrl());
                }
            }
            List<String> roles = roleMapper.searchAllRoleCodeByUserId(userId);
            List<String> permission = permissionMapper.searchAllPermissionCodeByUserId(userId);

            adminUserVO.setRoles(roles);
            adminUserVO.setButtons(permission);
            return adminUserVO;
        }).orElseThrow(AuthExceptionEnum.USER_NO::getAuthException);
    }

    public void create(UserCreateRO userCreateRO) {
        final var user = User.builder()
                .userAccount(userCreateRO.getUserAccount())
                .userName(userCreateRO.getUserName())
                .userEmail(userCreateRO.getEmail())
                .password(passwordEncoder.encode(userCreateRO.getPassword()))
                .frequency(userCreateRO.getFrequency())
                .build();
        baseMapper.insert(user);
    }

    public void reset(String id) {
        User user = User.builder().id(id)
                .password(passwordEncoder.encode("123456"))
                .build();
        userProvider.updatePassword(user);
    }

    @Override
    public UserRouterVO getUserRoutes() {
        String userId = SecurityUtils.getUserId();
        List<Menu> menus = menuMapper.getAllByUserId(userId);
        String userHomePage = menuMapper.getUserHomePage(userId);
        UserRouterVO userRouterVO = new UserRouterVO();
        userRouterVO.setHome(Optional.of(userHomePage).orElse("home"));
        userRouterVO.setRoutes(buildMenuTree(menus, null));
        return userRouterVO;
    }

    @Override
    public List<RouteVO> getConstantRoutes() {
        List<Menu> menus = menuMapper.selectList(new LambdaQueryWrapper<Menu>()
                .eq(Menu::getStatus, "1")
                .eq(Menu::getConstant, 1)
                .eq(Menu::getMenuType,"2")
        );
        return menus.stream().map(RouteVO::build).toList();
    }


    private List<RouteVO> buildMenuTree(List<Menu> menus, String parentId){
        return menus.stream()
                .filter(menu -> StringUtils.equals(menu.getParentId(),parentId))
                .map(menu -> {
                    List<RouteVO> children = buildMenuTree(menus, menu.getId());
                    RouteVO build = RouteVO.build(menu);
                    build.setChildren(children);
                    return build;
                })// 按 order 排序
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUser(UserAddRO userAddRO) {
        User user = User.builder().build();
        BeanUtils.copyProperties(userAddRO, user);
        baseMapper.insert(user);
        insertUserRole(user.getId(), userAddRO.getUserRoles());
    }

    @Override
    public UserVO getUser(String id) {
        User user = baseMapper.selectById(id);
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeUser(String[] ids) {
        baseMapper.deleteBatchIds(ListUtil.toList(ids));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editUser(UserEditRO userEditRO) {
        User user = User.builder().build();
        BeanUtils.copyProperties(userEditRO, user);
        baseMapper.updateById(user);

        userRoleMapper.delete(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, user.getId()));

        insertUserRole(user.getId(), userEditRO.getUserRoles());
    }

    private void insertUserRole(String userId, List<String> userRoles) {
        roleMapper.selectList(new LambdaQueryWrapper<Role>().in(Role::getRoleCode, userRoles))
                .stream()
                .map(role -> {
                    UserRole userRole = new UserRole();
                    userRole.setUserId(userId);
                    userRole.setRoleId(role.getId());
                    return userRole;
                }).forEach(userRoleMapper::insert);
    }
}




